#ifndef __VECTOR3_H_20210127_
#define __VECTOR3_H_20210127_

#include <math.h>

template<typename RT>
struct Vector3
{
    RT x, y, z;

    Vector3() {}
    Vector3(const Vector3 &a) : x(a.x), y(a.y), z(a.z) {}
    Vector3(RT nx, RT ny, RT nz) : x(nx), y(ny), z(nz) {}

    Vector3 &operator=(const Vector3 &a)
    {
        x = a.x;
        y = a.y;
        z = a.z;
        return *this;
    }

    bool operator==(const Vector3 &a) const
    {
        return (x == a.x && y == a.y && z == a.z);
    }

    bool operator!=(const Vector3 &a) const
    {
        return (x != a.x || y != a.y || z != a.z);
    }

    Vector3 &zero()
    {
        x = y = z = 0.0;
        return *this;
    }

    Vector3 operator-() const
    {
        return Vector3(-x, -y, -z);
    }

    Vector3 operator+(const Vector3 &a) const
    {
        return Vector3(x + a.x, y + a.y, z + a.z);
    }

    Vector3 operator-(const Vector3 &a) const
    {
        return Vector3(x - a.x, y - a.y, z - a.z);
    }

    template<typename RT2>
    Vector3 operator*(RT2 a) const
    {
        return Vector3(x * a, y * a, z * a);
    }

    template<typename RT2>
    Vector3 operator/(RT2 a) const
    {
        a = (RT)1.0 / a;
        return Vector3(x * a, y * a, z * a);
    }

    Vector3 &operator+=(const Vector3 &a)
    {
        x += a.x;
        y += a.y;
        z += a.y;
        return *this;
    }

    Vector3 &operator-=(const Vector3 &a)
    {
        x -= a.x;
        y -= a.y;
        z -= a.y;
        return *this;
    }

    template<typename RT2>
    Vector3 &operator*=(RT2 a)
    {
        x *= a;
        y *= a;
        z *= a;
        return *this;
    }

    template<typename RT2>
    Vector3 &operator/=(RT2 a)
    {
        a = (RT)1.0 / a;
        x *= a;
        y *= a;
        z *= a;
        return *this;
    }

    Vector3 &normalize()
    {
        RT magSq = x * x + y * y + z * z;
        if (magSq > (RT)0.0)
        {
            return operator/=(sqrt(magSq));
        }
        return zero();
    }

    // dot product
    RT operator*(const Vector3 &a) const
    {
        return x * a.x + y * a.y + z * a.z;
    }
};

template<typename RT>
inline RT vectorMag(const Vector3<RT> &a)
{
    return sqrt(a.x * a.x + a.y * a.y + a.z * a.z);
}

template<typename RT>
inline Vector3<RT> cross(const Vector3<RT> &a, const Vector3<RT> &b)
{
    return Vector3(
        a.y * b.z - a.z * b.y,
        a.z * b.x - a.x * b.z,
        a.x * b.y - a.y * b.x);
}

template<typename RT, typename RT2>
inline Vector3<RT> operator*(RT2 k, const Vector3<RT> &v)
{
    return Vector3(k * v.x, k * v.y, k * v.z);
}

template<typename RT>
inline RT distance(const Vector3<RT> &a, const Vector3<RT> &b)
{
    auto dx = a.x - b.x;
    auto dy = a.y - b.y;
    auto dz = a.z - b.z;
    return sqrt(dx * dx + dy * dy + dz * dz);
}

extern const Vector3<float> kZeroVector;

#endif